package stagebase

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
	"wtester/config"
	"wtester/pkg/parabase"
	"wtester/pkg/resultbase"
	"wtester/pkg/taskbase"
)

/**
  @author: weicai
  @since: 2024/3/11
  @desc: 任务执行者
**/

// Actuator 任务执行者
// 1. Loop 只有在IsSequential为true时才有效，表示执行的轮次
// 2. Loop 优先级高于stage的Duration，如果设置了Loop，则Duration无效；但是其优先级低于IsMaster，如果主线程结束则所有线程都结束
// 3. IsMaster 只有在IsSequential为true时才有效，表示是否是master线程，不允许存在多个master线程；如果IsMaster=true，则IsSequential=true
// 4. FixConcurrency 优先级高于 Weight 权重
type Actuator struct {
	Tasks          []taskbase.TaskInterface               // 任务列表
	Name           string                                 // 本阶段的名称
	Loops          int                                    // 测试执行轮次
	IsSequential   bool                                   // 是否需要顺序执行
	IsMaster       bool                                   // 是否是master线程，不允许存在多个master线程
	Weight         float32                                // 权重，0-100之间的值
	FixConcurrency uint8                                  // 固定的并发数（优先于权重）
	Constant       float32                                // 每次执行任务之前的等待时间，为固定的停顿时间，单位为秒
	Between        []float32                              // 每次执行任务之间的等待时间，为停顿范围，单位为秒
	ParamRange     map[string]*chan string                // 阶段共享的参数池，迭代型参数
	ParamRandom    map[string]parabase.TaskParamInterface // 阶段共享的参数池，随机型参数

	stage          *Stage                   // 所属阶段
	curConcurrency uint8                    // 已经启动的并发数
	isStop         bool                     // 任务是否执行结束
	wightTask      []taskbase.TaskInterface // 权重任务, 将任务转化成带有权重的任务，在随机执行时使用
}

func (a *Actuator) GetIsSequential() bool {
	return a.IsSequential || a.IsMaster
}

// 任务之间停顿的时间
func (a *Actuator) sleep() {
	constant := a.Constant
	if constant == 0 && len(a.Between) == 2 {
		startEnd := a.Between[1] - a.Between[0]
		startEnd10 := startEnd / 10
		constant = rand.Float32()*(startEnd10) + a.Between[0]
	}
	time.Sleep(time.Duration(constant * float32(time.Second)))
}

// 停止任务
func (a *Actuator) stop() {
	for range *a.stage.finishSignal {
		if a.isStop {
			break
		}
	}
	a.isStop = true
}

func (a *Actuator) run(task taskbase.TaskInterface, params *map[string]string, results *chan *resultbase.WtesterResults) {
	c := a.curConcurrency
	now := time.Now()
	task.Run(params)
	fmt.Println("task run time: ", time.Since(now))
	*results <- &resultbase.WtesterResults{ResponseTime: float64(time.Since(now)), Concurrences: c, Name: task.GetName()}
}

func (a *Actuator) getParam(p parabase.TaskParamInterface) string {
	switch p.GetScope() {
	case parabase.TaskParamScopeGlobal:
		if pa, ok := a.stage.ParamRandom[p.GetName()]; ok {
			return pa.Random()
		}
		a.stage.ParamRandom[p.GetName()] = p
		return p.Random()
	case parabase.TaskParamScopeUser:
		if pa, ok := a.ParamRandom[p.GetName()]; ok {
			return pa.Random()
		}
		a.ParamRandom[p.GetName()] = p
		return p.Random()
	default:
		return p.Random()
	}
}

func (a *Actuator) runTask(task taskbase.TaskInterface, results *chan *resultbase.WtesterResults) {
	fmt.Println("Actuator runTask")
	parameter := task.GetParameter()
	if parameter == nil || len(parameter) == 0 {
		a.run(task, nil, results)
		return
	}
	if !task.HasRangeParam() {
		params := make(map[string]string)
		for _, p := range parameter {
			if p.GetRely() == nil {
				params[p.GetName()] = a.getParam(p)
			}
		}
		for _, p := range parameter {
			r := p.GetRely()
			if r == nil {
				continue
			}
			if v, ok := params[r.Name]; ok {
				params[p.GetName()] = p.RelyValue(v)
			} else {
				config.Logger.Error(fmt.Sprintf("参数依赖错误, 依赖的参数不存在: %s, %s", p.GetName(), r.Name))
			}
		}
		a.run(task, &params, results)
		return
	}
	var group sync.WaitGroup
	// 获取range参数
	data := make(map[string]chan string)
	for _, p := range parameter {
		if p.GetKind() == parabase.TaskParamKindRange {
			c := make(chan string)
			data[p.GetName()] = c
			group.Add(1)
			go func() {
				defer group.Done()
				p.Generator(&c)
				close(c)
			}()
		}
	}
	group.Add(1)
	// 启动任务
	go func() {
		defer group.Done()
		isFinish := false
		for {
			params := make(map[string]string)
			for k, d := range data {
				if v, ok := <-d; ok {
					params[k] = v
				} else {
					isFinish = true
					break
				}
			}
			if isFinish {
				break
			}
			for _, p := range parameter {
				if p.GetKind() == parabase.TaskParamKindRandom {
					params[p.GetName()] = p.Random()
				}
			}
			a.run(task, &params, results)
		}
	}()
	group.Wait()
}

// 顺序执行任务
func (a *Actuator) runSequential(results *chan *resultbase.WtesterResults) {
	for i := 0; i < a.Loops; i++ {
		if a.isStop {
			break
		}
		for _, task := range a.Tasks {
			if a.isStop {
				break
			}
			if task == nil {
				continue
			}
			a.runTask(task, results)
			a.sleep()
		}
	}
	a.isStop = true
	if a.stage.finishSignal != nil {
		close(*a.stage.finishSignal)
	}
}

// 随机执行任务
func (a *Actuator) runRandom(results *chan *resultbase.WtesterResults) {
	length := len(a.Tasks)
	for {
		if a.isStop {
			break
		}
		a.runTask(a.Tasks[rand.Intn(length)], results)
		a.sleep()
	}
}

// Run 启动任务
func (a *Actuator) Run(results *chan *resultbase.WtesterResults) {
	fmt.Println("Actuator Run")
	var group sync.WaitGroup
	// 当前线程不是主线程且未指定测试运行的轮次的测试，则监测结束信号
	if !a.IsMaster && a.Loops <= 0 {
		group.Add(1)
		go func() {
			a.stop()
			group.Done()
		}()
	}
	// 启动任务
	group.Add(1)
	go func() {
		defer group.Done()
		if a.IsMaster {
			a.runSequential(results)
			return
		}
		if a.GetIsSequential() {
			a.runSequential(results)
		} else {
			a.runRandom(results)
		}
	}()
	group.Wait()
}

// InitActuator 初始化任务执行者
func InitActuator(name string, isSequential, isMaster bool, loops int, fixConcurrency uint8, weight, constant float32, between []float32, task []taskbase.TaskInterface) *Actuator {
	length := len(between)
	if length != 0 && length != 2 {
		panic("between参数只能是0个或2个")
	}
	if weight < 0 {
		panic("weight参数不能小于0")
	}
	if name == "" {
		panic("name参数不能为空")
	}
	if isMaster || loops > 0 {
		isSequential = true
	}
	if fixConcurrency <= 0 && weight <= 0 {
		panic("fixConcurrency和weight不能同时为0")
	}
	return &Actuator{Name: name, IsSequential: isSequential, IsMaster: isMaster, Loops: loops, FixConcurrency: fixConcurrency, Weight: weight, Constant: constant, Between: between, Tasks: task}
}
